package logic

import (
	"context"
	menuApi "mldong/api/menu"
	"mldong/api/rbac"
	"mldong/api/user"
	"mldong/internal/modules/sys/dao"
	"mldong/internal/modules/sys/entity"
	service "mldong/internal/modules/sys/service"
	"mldong/utility"

	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/jinzhu/copier"
)

func init() {
	// 注册服务实现
	utility.Register(service.RbacServiceType, NewRbacLogic())
}
func NewRbacLogic() service.IRbacService {
	return &RbacLogic{
		menuDao:     dao.NewMenuDao(),
		roleDao:     dao.NewRoleDao(),
		roleMenuDao: dao.NewRoleMenuDao(),
		userRoleDao: dao.NewUserRoleDao(),
	}
}

type RbacLogic struct {
	menuDao     *dao.MenuDao
	roleDao     *dao.RoleDao
	roleMenuDao *dao.RoleMenuDao
	userRoleDao *dao.UserRoleDao
}

// 获取某个用户拥有的角色ID集合
func (logic *RbacLogic) GetRoleIdsByUserId(ctx context.Context, userId string) (res []string, err error) {
	userRoles, err := logic.userRoleDao.GetByUserId(userId)
	if err != nil {
		return res, err
	}
	roleIds := make([]string, 0)
	for _, userRole := range userRoles {
		roleIds = append(roleIds, *userRole.RoleId)
	}
	return roleIds, nil

}

// 获取某个角色拥有菜单权限
func (logic *RbacLogic) GetMenuCodesByRoleId(ctx context.Context, roleId string) (res []string, err error) {
	menuCodes := []string{}
	menuIds, err := logic.RoleMenuIds(ctx, roleId)
	if err != nil {
		return menuCodes, err
	}
	if len(menuIds) > 0 {
		menuCodes, err = logic.menuDao.GetMenuCodesByIds(menuIds)
		if err != nil {
			return menuCodes, err
		}
	}
	return menuCodes, nil
}

// 获取某个用户的拥有菜单权限
func (logic *RbacLogic) GetMenuIdsByUserId(ctx context.Context, userId string) (res []string, err error) {

	roleIds, err := logic.GetRoleIdsByUserId(ctx, userId)
	if err != nil {
		return res, err
	}
	return logic.roleMenuDao.GetMenuIdsByRoleIds(roleIds)
}

// 删除用户角色关联
func (logic *RbacLogic) RemoveUserRole(ctx context.Context, params []rbac.UserRoleParam) (res bool, err error) {
	if len(params) == 0 {
		return true, nil
	}
	g.DB().Transaction(context.Background(), func(ctx context.Context, tx gdb.TX) error {

		for _, param := range params {
			_, err = logic.userRoleDao.Remove(param.UserId, param.RoleId)
			if err != nil {
				return err
			}
		}
		return nil
	})
	return true, err
}

// 获取某个角色菜单ID
func (r *RbacLogic) RoleMenuIds(ctx context.Context, roleId string) (res []string, err error) {
	roleMenuList, err := r.roleMenuDao.List(func(m *gdb.Model) *gdb.Model {
		return m.Where("role_id", roleId)
	})
	if err != nil {
		return res, err
	}
	for _, roleMenu := range roleMenuList {
		res = append(res, *roleMenu.MenuId)
	}
	return res, err
}

// 获取某个角色菜单列表
func (logic *RbacLogic) RoleMenuList(ctx context.Context, roleId string) (res []menuApi.MenuVO, err error) {
	var menuVOS = []menuApi.MenuVO{}
	menuIds, err := logic.RoleMenuIds(ctx, roleId)
	if err != nil {
		return menuVOS, err
	}
	if len(menuIds) == 0 {
		return menuVOS, nil
	}
	menus, err := logic.menuDao.List(func(m *gdb.Model) *gdb.Model {
		m = m.WhereIn("id", menuIds)
		m = m.OrderAsc("sort")
		return m
	})
	if err != nil {
		return menuVOS, err
	}
	copier.Copy(&menuVOS, &menus)
	return menuVOS, nil

}

// 保存角色菜单关联
func (logic *RbacLogic) SaveRoleMenu(ctx context.Context, params []rbac.RoleMenuParam) (res bool, err error) {
	if len(params) == 0 {
		return true, nil
	}
	roleId := params[0].RoleId
	g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		roleMenuModel := logic.roleMenuDao.Model(tx)
		// 删除用户拥的菜单
		_, err := roleMenuModel.Where("role_id", roleId).Delete()
		if err != nil {
			return err
		}
		for _, param := range params {
			roleMenu := entity.RoleMenu{
				RoleId: &param.RoleId,
				MenuId: &param.MenuId,
			}
			_, err := logic.roleMenuDao.Insert(roleMenu, tx)
			if err != nil {
				return err
			}
		}
		return nil
	})
	return true, nil
}

// 保存用户角色关联
func (logic *RbacLogic) SaveUserRole(ctx context.Context, params []rbac.UserRoleParam) (res bool, err error) {
	if len(params) == 0 {
		return true, nil
	}
	g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		for _, param := range params {
			exists, _ := logic.userRoleDao.Exists(param.UserId, param.RoleId)
			if exists {
				continue
			}
			userRole := entity.UserRole{
				UserId: &param.UserId,
				RoleId: &param.RoleId,
			}
			_, err = logic.userRoleDao.Insert(userRole, tx)
			if err != nil {
				return err
			}
		}
		return nil
	})
	return true, nil
}

// 角色ID获取用户列表
func (logic *RbacLogic) UserListByRoleId(ctx context.Context, req *user.PageReq) (res user.UserPageVO, err error) {
	userRole, err := logic.userRoleDao.GetByRoleId(req.RoleId)
	if err != nil {
		return
	}
	userRoleIds := make([]string, len(userRole))
	for i := range userRole {
		userRoleIds[i] = *userRole[i].UserId
	}
	if len(userRoleIds) == 0 {
		userRoleIds = append(userRoleIds, "0")
	}
	req.InUserIdList = userRoleIds
	userService := utility.MustResolve(service.UserServiceType).(service.IUserService)
	return userService.Page(ctx, req)
}

// 获取用户列表-排除指定角色
func (logic *RbacLogic) UserListExcludeRoleId(ctx context.Context, req *user.PageReq) (res user.UserPageVO, err error) {
	userRole, err := logic.userRoleDao.GetByRoleId(req.RoleId)
	if err != nil {
		return
	}
	userRoleIds := make([]string, len(userRole))
	for i := range userRole {
		userRoleIds[i] = *userRole[i].UserId
	}
	req.NotInUserIdList = userRoleIds
	userService := utility.MustResolve(service.UserServiceType).(service.IUserService)
	return userService.Page(ctx, req)
}
